package cn.shop.merchant.service;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.DigestUtil;
import cn.shop.merchant.mapper.user.MerchantUserRoleMapper;
import cn.shop.merchant.model.user.MerchantRole;
import cn.shop.merchant.model.user.MerchantUserRole;
import cn.shop.merchant.model.user.UserInfoForSession;
import cn.shop.merchant.req.AutoLoginInfoReq;
import cn.shop.merchant.req.IdCardReq;
import cn.shop.merchant.req.MerchantUserQueryReq;
import cn.shop.merchant.req.PhoneCheckReq;
import cn.shop.merchant.req.TransactionPasswordReq;
import cn.shop.thiredparty.entity.AuthResult;
import cn.shop.thiredparty.entity.IdAuthCode;
import cn.shop.thiredparty.service.ThiredpartyService;
import cn.shop.transcation.merchantRateModule.entity.MerchantRate;
import cn.shop.transcation.merchantRateModule.mapper.MerchantRateMapper;
import cn.shop.transcation.merchantUser.mapper.MerchantUserMapper;
import cn.shop.transcation.merchantUser.mapper.entity.MerchantUser;
import cn.shop.utils.ApiResult;
import cn.shop.utils.PageUtil;
import cn.shop.utils.Putif;
import cn.shop.utils.constant.Constant;
import cn.shop.utils.constant.Crypto;
import cn.shop.utils.constant.SMSVerificationCode;
import cn.shop.utils.exception.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.shop.utils.constant.Constant.UID;

/**
 * @author : 李一涵
 * @date : Created in 2021-11-11 13:35
 * @description :
 */
@Service
public class MerchantUserService extends BaseServiceImpl<MerchantUserMapper, MerchantUser> {

	@Resource
	private MerchantRoleService roleService;
	@Resource
	private MerchantUserRoleMapper userRoleMapper;

	@Resource
	private MerchantRateMapper rateMapper;

	@Resource
	private ThiredpartyService thiredpartyService;

	@Transactional(rollbackFor = Exception.class)
	public boolean regist(MerchantUser user) {
		int result = baseMapper.insert(user);
		MerchantRole role = roleService.getOne(Wrappers.<MerchantRole>lambdaQuery().eq(MerchantRole::getUid, 0));
		if (role != null) {
			MerchantUserRole r = new MerchantUserRole();
			r.setAccountId(user.getId());
			r.setRoleId(role.getId());
			r.setUid(user.getId());
			userRoleMapper.insert(r);
		}
		return SqlHelper.retBool(result);
	}

	/**
	 * 设置交易密码
	 *
	 * @param transactionPasswordReq
	 * @return
	 * @author jzw
	 * @since 2022/2/23 14:59
	 */
	public boolean setTransactionPassword(TransactionPasswordReq transactionPasswordReq) {

		SaSession session = SaSessionCustomUtil.getSessionById(SMSVerificationCode.TRANSACTION_PASSWORD_KEY + transactionPasswordReq.getPhone());
		if (session != null) {
			String code = (String) session.get("code");
			if (code != null && code.equalsIgnoreCase(transactionPasswordReq.getCode())) {
				session.logout();
				LambdaUpdateWrapper<MerchantUser> updateWrapper = Wrappers.lambdaUpdate();
				updateWrapper.set(MerchantUser::getTransactionPassword, DigestUtil.bcrypt(transactionPasswordReq.getPassword()))
						.eq(MerchantUser::getId, StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId())
						.eq(MerchantUser::getUid, 0L);
				return SqlHelper.retBool(baseMapper.update(new MerchantUser(), updateWrapper));
			}
		}
		throw new ServiceException("验证码不正确");
	}

	/**
	 * 获取当前登录人的按钮权限信息
	 *
	 * @return java.util.List<java.lang.String>
	 * @author jy
	 * @since 2020/8/24 21:51
	 */
	public List<String> getAllBtnPermissionByUserId() {
		return baseMapper.getAllBtnPermissionByUserId(StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId());
	}

	/**
	 * 获取当前登录用户的权限url
	 *
	 * @return java.util.List<java.lang.String>
	 */
	public List<String> getAllPermissionUrlByUserId() {
		return baseMapper.getAllPermissionUrlByUserId(StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId());
	}

	public boolean hasAccount(String account) {
		long count = count(Wrappers.<MerchantUser>lambdaQuery()
				.eq(MerchantUser::getAccount, account));
		return count > 0;
	}

	/**
	 * 获取管理员列表
	 *
	 * @param uid       商户编号
	 * @param loginId   当前登录用户编号
	 * @param req       查询条件
	 */
	public Map<String, Object> listUser(long uid, long loginId, MerchantUserQueryReq req) {
		Page<MerchantUser> page = Page.of(req.getCurrent(), req.getSize());
		LambdaQueryWrapper<MerchantUser> query = Wrappers.<MerchantUser>lambdaQuery()
				.eq(MerchantUser::getUid, uid)
				.ne(MerchantUser::getId, loginId)
				.apply(notNull.test(req.getStartTime()), req.gtTime("create_time"), req.getStartTime())
				.apply(notNull.test(req.getEndTime()), req.ltTime("create_time"), req.getEndTime())
				.orderBy(true, isAsc.test(req.getOrderByTime()), MerchantUser::getId);
		page = page(page, query);
		return PageUtil.formatPage(page, this::format);
	}

	/**
	 * 通过用户名和密码获取用户信息
	 *
	 * @param account  账号
	 * @param password 密码
	 */
	public MerchantUser login(String account, String password) {
		if (StrUtil.isBlank(account) || StrUtil.isBlank(password)) {
			return null;
		}
		MerchantUser user = getOne(Wrappers.<MerchantUser>lambdaQuery()
				.eq(MerchantUser::getAccount, account));
		if (user == null) {
			return null;
		}
		if (!DigestUtil.bcryptCheck(password, user.getPassword())) {
			return null;
		}
		return user;
	}

	/**
	 * 更新管理员信息
	 *
	 * @param uid  商户编号
	 * @param user 管理员信息
	 */
	public boolean updateUser(long uid, MerchantUser user) {
		user.setAccount(null);
		return update(user, Wrappers.<MerchantUser>lambdaUpdate()
				.eq(MerchantUser::getId, user.getId()));
	}

	/**
	 * 删除管理员
	 *
	 * @param ids 编号，多个用逗号隔开
	 * @param uid 商户编号
	 */
	public boolean deleteUser(long[] ids, long uid) {
		List<Long> idList = Arrays.stream(ids).boxed().collect(Collectors.toList());
		return SqlHelper.retBool(getBaseMapper().batchDeleteWithFill(new MerchantUser(), Wrappers.<MerchantUser>lambdaUpdate()
				.eq(MerchantUser::getUid, uid)
				.in(MerchantUser::getId, idList)));
	}

	/**
	 * 变更管理员状态
	 *
	 * @param ids    编号，多个用逗号隔开
	 * @param uid    商户编号
	 * @param status 状态
	 */
	public boolean changeStatus(long[] ids, long uid, Integer status) {
		MerchantUser update = new MerchantUser();
		update.setState(status);
		return update(update, Wrappers.<MerchantUser>lambdaUpdate()
				.in(MerchantUser::getId, Arrays.stream(ids).boxed().collect(Collectors.toList()))
				.eq(MerchantUser::getUid, uid));
	}

	/**
	 * 获取管理员信息
	 *
	 * @param id  编号
	 * @param uid 商户编号
	 */
	public Map<String, Object> getUser(long id, long uid) {
		MerchantUser user = getOne(Wrappers.<MerchantUser>lambdaQuery()
				.eq(MerchantUser::getId, id)
				.eq(MerchantUser::getUid, uid));
		return Putif.start()
				.anyway("id", user.getId())
				.anyway("account", user.getAccount())
				.anyway("name", user.getName())
				.anyway("gender", user.getGender())
				.anyway("phone", user.getPhone())
				.anyway("state", user.getState())
				.anyway("createTime", user.getCreateTime())
				.get();
	}

	/**
	 * 获取管理员信息
	 *
	 * @param id  编号
	 */
	public Map<String, Object> getUser(long id) {
		MerchantUser user = getOne(Wrappers.<MerchantUser>lambdaQuery()
				.eq(MerchantUser::getId, id));
		return Putif.start()
				.anyway("id", user.getId())
				.anyway("account", user.getAccount())
				.anyway("name", user.getName())
				.anyway("gender", user.getGender())
				.anyway("phone", user.getPhone())
				.anyway("state", user.getState())
				.anyway("balance", user.getBalance())
				.anyway("identityCard", DesensitizedUtil.idCardNum(user.getIdentityCard(), 3, 4))
				.anyway("createTime", user.getCreateTime())
				.get();
	}

	public List<String> roleCodeByUserId(Long id) {
		return baseMapper.selectRoleCodeByUserId(id);
	}

	/**
	 * 格式化管理员信息
	 *
	 * @param user 管理员信息
	 */
	private Map<String, Object> format(MerchantUser user) {
		return Putif.start()
				.anyway("id", user.getId())
				.anyway("account", user.getAccount())
				.anyway("name", user.getName())
				.anyway("gender", user.getGender())
				.anyway("phone", user.getPhone())
				.anyway("state", user.getState())
				.anyway("createTime", user.getCreateTime())
				.get();
	}

	/**
	 * 自动登录
	 *
	 * @param autoLoginInfoReq
	 * @return
	 * @author jzw
	 * @since 2022/2/24 14:43
	 */
	public ApiResult<Map<String, Object>> automaticLogin(AutoLoginInfoReq autoLoginInfoReq) {
		try {
			RSA rsa = SecureUtil.rsa(Crypto.PRIVATE_KEY, null);
			String info = rsa.decryptStr(autoLoginInfoReq.getToken(), KeyType.PrivateKey);
			List<String> split = StrUtil.split(info, ',');
			if (split.size() == 2) {
				LambdaQueryWrapper<MerchantUser> query = Wrappers.lambdaQuery();
				query.eq(MerchantUser::getAccount, split.get(1)).eq(MerchantUser::getPassword, split.get(0));
				MerchantUser user = baseMapper.selectOne(query);
				if (user != null) {
					StpUtil.login(user.getId());
					SaTokenInfo token = StpUtil.getTokenInfo();
					StpUtil.getSession().set(Constant.USER, new UserInfoForSession(user.getId(), user.getAccount(), user.getName()))
							.set(UID, user.getUid() == 0 ? user.getId() : user.getUid());
					Map<String, Object> data = Putif.start()
							.anyway("tokenName", token.getTokenName())
							.anyway("username", user.getAccount())
							.anyway("token", token.getTokenValue())
							.anyway("isParent", user.getUid() == 0)
							.get();
					return ApiResult.success("登陆成功", data);
				}
			}
			return ApiResult.fail("自动登录失败,请手动登录!");
		} catch (Exception e) {
			return ApiResult.fail("自动登录失败,请手动登录!");
		}
	}

	public boolean setPhone(PhoneCheckReq phoneCheckReq) {
		SaSession session = SaSessionCustomUtil.getSessionById(SMSVerificationCode.PHONE_PASSWORD_KEY + phoneCheckReq.getPhone());
		if (session != null) {
			String code = (String) session.get("code");
			if (code != null && code.equalsIgnoreCase(phoneCheckReq.getCode())) {
				session.logout();
				LambdaUpdateWrapper<MerchantUser> updateWrapper = Wrappers.lambdaUpdate();
				updateWrapper.set(MerchantUser::getPhone, phoneCheckReq.getPhone())
						.eq(MerchantUser::getId, StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId())
						.eq(MerchantUser::getUid, 0L);
				return SqlHelper.retBool(baseMapper.update(new MerchantUser(), updateWrapper));
			}
		}
		throw new ServiceException("验证码不正确");
	}

	public ApiResult<Map<String, Object>> setIdCard(IdCardReq idCardReq) {
		AuthResult result = thiredpartyService.idCardAuth(idCardReq.getIdentityCard(), idCardReq.getName());

		if (!IdAuthCode.C10000.code().equals(result.getCode())) {
			return ApiResult.fail("实名认证失败");
		}
		if (!AuthResult.State.SAME.code().equals(result.getData().getResult())) {
			return ApiResult.fail("请检查身份证与姓名是否一致");
		}
		LambdaUpdateWrapper<MerchantUser> updateWrapper = Wrappers.lambdaUpdate();
		updateWrapper.set(MerchantUser::getName, idCardReq.getName())
				.set(MerchantUser::getIdentityCard, idCardReq.getIdentityCard())
				.eq(MerchantUser::getId, StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId())
				.eq(MerchantUser::getUid, 0L);

		return ApiResult.result(SqlHelper.retBool(baseMapper.update(new MerchantUser(), updateWrapper)));
	}

	public MerchantRate getRate(long id) {
		return rateMapper.selectById(id);
	}

	public boolean updateRate(long id, MerchantRate rate) {
		return SqlHelper.retBool(rateMapper.update(new MerchantRate(), Wrappers.<MerchantRate>lambdaUpdate()
				.set(MerchantRate::getRateAdditional, rate.getRateAdditional())
				.set(MerchantRate::getSingleAdditional, rate.getSingleAdditional())
				.eq(MerchantRate::getId, id)));
	}
}
